﻿using System;
using System.Linq;
using System.Globalization;
using System.Net;
using System.ServiceModel;
using Stimulus360.CrmService;
using Stimulus360.Models.Data;
using Microsoft.ServiceModel.Web;
using System.Collections.Generic;
using Stimulus360.Services.Interfaces;
using Idea = Stimulus360.Models.Data.Idea;

namespace Stimulus360.Services
{
    /// <summary>
    ///     The Sync Service is responsible for syncing ideas between the StimulusDB
    ///     and the CRM DB
    /// </summary>
    public class CrmSyncService : ICrmSyncService
    {
        #region ICRMSyncService Members

        /// <summary>
        ///     Instance of IdeaService to be used
        /// </summary>
        private readonly IIdeaService _ideaService;

        /// <summary>
        ///     Instance of ProjectService to be used
        /// </summary>
        private readonly IProjectService _projectService;

        /// <summary>
        ///     Default constructor. instatiates default IdeaService and project service
        /// </summary>
        public CrmSyncService()
        {
            try
            {
                _ideaService = new IdeaService();
                _projectService = new ProjectService();

            }
            catch (NullReferenceException exception)
            {
                // Exception conditions detected when invoking this service as a RESTFul service need to report an HTTP status code in the 
                //  400-500   range in addition to a message that details the exception
                throw new WebProtocolException(HttpStatusCode.BadGateway, "Could not connect to the database.Please check the connection and try again", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }


        /// <summary>
        ///     Overloaded constructor for dependency injection
        /// </summary>
        /// <param name="ideaService">
        ///     The IIdeaService instance to be used
        /// </param>
        /// <param name="projectService">
        ///     The IProjectService instance to be used.
        /// </param>
        public CrmSyncService(IIdeaService ideaService, IProjectService projectService)
        {
            try
            {
                _ideaService = ideaService ?? new IdeaService();
                _projectService = projectService ?? new ProjectService();

            }
            catch (NullReferenceException exception)
            {
                // Exception conditions detected when invoking this service as a RESTFul service need to report an HTTP status code in the 
                //  400-500   range in addition to a message that details the exception
                throw new WebProtocolException(HttpStatusCode.BadGateway, "Could not connect to the database.Please check the connection and try again", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     This method is responsible to sync ideas from the CRM database
        ///     to the Stimulus DB
        /// </summary>
        public void SyncFromCrm()
        {
            CrmDataServiceClient serviceClient = new CrmDataServiceClient();
            List<Idea> successfullySynced = new List<Idea>();

            try
            {
                List<Idea> syncedIdeas = ConvertToStimulusDbEntity(serviceClient.GetSyncedIdeas()).ToList();

                var acceptedList = from idea in syncedIdeas
                                   where idea.Status == (byte)IdeaStatus.Accepted
                                   select idea;
                var rejectedList = from rejected in syncedIdeas
                                   where rejected.Status == (byte)IdeaStatus.Rejected
                                   select rejected;
                List<Idea> acceptedIdeas = acceptedList.ToList();
                List<Idea> rejectedIdeas = rejectedList.ToList();

                foreach (Idea idea in acceptedIdeas)
                {
                    CreateProjectForIdea(idea);
                    successfullySynced.Add(idea);
                }
                foreach (Idea idea in rejectedIdeas)
                {
                    _ideaService.UpdateStatus(idea, IdeaStatus.Rejected);
                    successfullySynced.Add(idea);
                }
                serviceClient.IdeasSuccessfullySynced(ConvertToCrmEntity(successfullySynced).ToArray());

            }
            catch (FaultException exception)
            {
                serviceClient.IdeasSuccessfullySynced(ConvertToCrmEntity(successfullySynced).ToArray());

                throw new WebProtocolException(HttpStatusCode.InternalServerError, "The Syncing process failed", exception);

            }
            catch (NullReferenceException exception)
            {
                serviceClient.IdeasSuccessfullySynced(ConvertToCrmEntity(successfullySynced).ToArray());

                throw new WebProtocolException(HttpStatusCode.InternalServerError, "One or more parameters was null", exception);
            }
            catch (Exception exception)
            {
                serviceClient.IdeasSuccessfullySynced(ConvertToCrmEntity(successfullySynced).ToArray());

                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }



        }

        /// <summary>
        ///     Method to create a project for an accepted Idea
        /// </summary>
        /// <param name="idea">
        ///     Idea which has been accepted.
        /// </param>
        private void CreateProjectForIdea(Idea idea)
        {
            Project project = new Project();
            project.CategoryId = idea.CategoryId;
            project.DateCreated = idea.StatusLastUpdatedOn.GetValueOrDefault();
            project.Status = (byte)ProjectStatus.InProgress;
            project.Title = idea.Title;
            project.Description = idea.Description;
            project.VolunteersRequired = false;
            project.ProjectManager = idea.SubmittedBy;
            int projectId = _projectService.Add(project);
            _ideaService.Accept(idea, projectId.ToString(CultureInfo.CurrentCulture));
        }

        /// <summary>
        ///     This method is responsible to sync ideas from the Stimulus DB to the
        ///     CRM Database.
        /// </summary>
        public void SyncToCrm()
        {
            try
            {
                //Get popular ideas from Stimulus 360 DB
                IEnumerable<Idea> ideaList = _ideaService.GetPopular();
                foreach (Idea idea in ideaList)
                {
                    _ideaService.UpdateStatus(idea, IdeaStatus.Reviewing);
                }
                CrmDataServiceClient serviceClient = new CrmDataServiceClient();
                serviceClient.AddIdeas(ConvertToCrmEntity(ideaList).ToArray());

            }
            catch (FaultException exception)
            {

                throw new WebProtocolException(HttpStatusCode.InternalServerError, "The Syncing process failed", exception);

            }
            catch (NullReferenceException exception)
            {

                throw new WebProtocolException(HttpStatusCode.InternalServerError, "One or more parameters was null", exception);
            }
            catch (Exception exception)
            {

                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     Method converts Idea Entity from the CRM DB to the Idea entity in the Stimulus DB.
        /// </summary>
        /// <param name="crmIdeaList">
        ///     List list from the CRM DB
        /// </param>
        /// <returns>
        ///     Corresponding ideas for the Stimulus DB
        /// </returns>
        private static IEnumerable<Idea> ConvertToStimulusDbEntity(IEnumerable<CrmService.Idea> crmIdeaList)
        {
            return crmIdeaList.Select(idea => new Idea
                                                      {
                                                          IdeaId = idea.IdeaId,
                                                          Title = idea.Title,
                                                          Description = idea.Description,
                                                          Status = idea.Status,
                                                          StatusLastUpdatedOn = idea.StatusLastUpdatedOn,
                                                          SubmittedBy = idea.SubmittedBy,
                                                          DateSubmitted = idea.DateSubmitted,
                                                          CategoryId = idea.CategoryId,
                                                          Comments = idea.Comments

                                                      });
        }

        /// <summary>
        ///     Method converts idea entity from the Stimulus DB to idea entity from the CRM DB
        /// </summary>
        /// <param name="ideaList">
        ///     List of Ideas from the Stimulus DB
        /// </param>
        /// <returns>
        ///     List of ideas fro the CRM DB
        /// </returns>
        private static IEnumerable<CrmService.Idea> ConvertToCrmEntity(IEnumerable<Idea> ideaList)
        {
            return ideaList.Select(idea => new CrmService.Idea
                                               {
                                                   IdeaId = idea.IdeaId,
                                                   Title = idea.Title,
                                                   Description = idea.Description,
                                                   Status = idea.Status,
                                                   StatusLastUpdatedOn = idea.StatusLastUpdatedOn,
                                                   SubmittedBy = idea.SubmittedBy,
                                                   DateSubmitted = idea.DateSubmitted,
                                                   CategoryId = idea.CategoryId,
                                                   Comments = idea.Comments,
                                                   AverageRating = idea.AverageRating,
                                                   UserRatingsCount = idea.UserRatingsCount,
                                                   FavoritesCount = idea.FavoritesCount.GetValueOrDefault(0)

                                               });

        }

        #endregion
    }
}
